/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.common.calculation.AmountUtil;
import com.common.calculation.InitiUtil;
import com.common.constant.AccountConstant;
import com.common.constant.PropertiesConstant;
import com.common.context.SpringContextUtils;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Branch;
import com.pojo.Customer;
import com.pojo.Sjno;
import com.pojo.Trancfg;
import com.pojo.Tranlist;
import com.pojo.Tranlog;
import com.service.DepositMoneyService;


@Service
@Scope("prototype")
public class DepositMoneyServiceImpl implements DepositMoneyService,Callable<Long> {

    private final Logger logger = LoggerFactory.getLogger(DepositMoneyServiceImpl.class); 

    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    
    @Autowired
    private Transfer transfer;
    @Autowired
    private PropertiesConstant propertiesConstant;
    private int num;
    
    private int datacfg_accnum;
    private String process_id;

    private String trancfgType;
    private Trancfg tf;

    /**
     * 单线程执行存款操作
     * @param num
     * @param datacfg_accnum
     * @throws Exception 
     */
    public Long call() throws Exception {
    	long countTime = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
        	Date nowDate = new Date();
        	long nowMillis = nowDate.getTime();
    		Time nowTime = new Time(nowMillis);
    		BigDecimal money = AmountUtil.getRandomMoney(0,AccountConstant.ACCOUNT_DEFAULT_MONERY);//模拟存款金额
            String accountTo = "60"+String.valueOf((new Random().nextInt(datacfg_accnum)+1)+InitiUtil.getAccnum(datacfg_accnum));//贷方
			Account to = null;
			try {
				to = transfer.readAccount(accountTo);
			} catch (Exception e) {
				e.printStackTrace();
    			logger.error("DepositMoneyServiceImpl withdraw readAccount error {} ", e);
			}
    		insertBreakPoint();
			if(to==null) {
				logger.info("DepositMoneyServiceImpl withdraw Account to {} is null ", accountTo);
				continue;
			}
			Tranlog tranLog = null;
          	int fromSeq = updateAccountBranchSeq(to);
          	if(fromSeq==AccountConstant.ACCOUNT_SEQ_FAILURE) {//更新贷方网点流水号失败
          		tranLog = new Tranlog(nowDate,to.getAccount_branchid(),fromSeq,trancfgType
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    			logger.error("DepositMoneyServiceImpl withdraw updateAccountBranchSeq from {} branch {} seq {} rollback ", accountTo,to.getAccount_branchid(),fromSeq);
	          	continue;
          	}else {
    			logger.info("DepositMoneyServiceImpl withdraw updateAccountBranchSeq from {} branch {} seq {} success ", accountTo,to.getAccount_branchid(),fromSeq);
          	}
          	try {
          		if(to!=null&&(to.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL)
    					||to.getAccount_stat().equals(AccountConstant.ACCOUNT_ONLYIN))) {
        			tranLog = withdrawMoney(accountTo,money,tranLog,fromSeq,nowDate,nowTime,nowMillis);//更新转账金额
        			if(tranLog==null) {//转账业务回滚
        				tranLog = new Tranlog(nowDate,to.getAccount_branchid(),fromSeq,trancfgType
                    			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
        	    		logger.error("DepositMoneyServiceImpl withdraw rollback accountFrom {} accountTo {}" 
        						,accountTo,accountTo);
        			}
        		}else {//贷方账户不正常
        			tranLog = new Tranlog(nowDate,to.getAccount_branchid(),fromSeq,trancfgType
                			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
        			logger.info("DepositMoneyServiceImpl withdraw Account to  {} not normal state is {} ",accountTo, to.getAccount_stat() );
        		}
			} catch (Exception e) {
				logger.error("DepositMoneyServiceImpl updateAccountBranchSeq run error brachid {} e {}" 
						,to.getAccount_branchid(),e);			
			}
            //插入transLog
          	insertAccountTranlog(tranLog,0);
        }
        return System.currentTimeMillis()-countTime;
	}

    /**
     * 插入断点
     */
    private void insertBreakPoint() {
    	if(tf!=null&&"Y".equals(tf.getTrancfg_brkpot())) {
    		try {
				Thread.sleep(Long.parseLong(tf.getTrancfg_brktime()));
			} catch (Exception e) {
		    	logger.error("DepositMoneyServiceImpl insertBreakPoint is run error e {} ",e);
			}
    	}
	}

	/**
     * 插入tranLog
     * @param tranLog
	 * @param repeat 
     */
    public void insertAccountTranlog(Tranlog tranLog, int repeat) {
    	boolean isSuccess = false;
    	TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		transfer.insertTranlog(tranLog);
    		logger.info("DepositMoneyServiceImpl insertAccountTranlog tranlog_date {} tranlog_branchid {} tranlog_branchseq {} success "
    				,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq());
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
        	isSuccess = true;
		} catch (Exception e) {
			logger.error("DepositMoneyServiceImpl insertAccountTranlog rollback tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
					,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("DepositMoneyServiceImpl insertAccountTranlog rollback error accountTo {} e {}",e);
			}
		}
    	if(isSuccess) {
    		LocalTranLogServiceImpl localTranLogServiceImpl = SpringContextUtils.getBean(LocalTranLogServiceImpl.class);
        	localTranLogServiceImpl.setTranlog(tranLog);
        	localTranLogServiceImpl.start();
    	}else {
			logger.error("========>重试次数"+repeat+"===>"+tranLog.getTranlog_branchid());
    		if(repeat<propertiesConstant.getRepeat()) {
    			try {
					Thread.sleep(500);
	    	 		insertAccountTranlog(tranLog,repeat+1);
				} catch (InterruptedException e) {
					logger.error("DepositMoneyServiceImpl insertAccountTranlog Thread error tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
							,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
				}
    		}
    	}
	}

	/**
     * 更新网点流水号
     * @param account
     */
    public int updateAccountBranchSeq(Account account) {
    	TransactionStatus transactionStatus = null;
    	int seq = AccountConstant.ACCOUNT_SEQ_FAILURE;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Branch branch = transfer.readTransactionalBranchSeq(account.getAccount_branchid());
        	seq = branch.getBranch_seq();
    		transfer.updateBranch(branch.getBranch_id());//更新网点流水号
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			e.printStackTrace();
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("DepositMoneyServiceImpl updateAccountBranchSeq rollback error account {} branchId {} e {}",account.getAccount_id(),account.getAccount_branchid(),e);
			}
	    	logger.error("DepositMoneyServiceImpl updateAccountBranchSeq rollback account {} branchId {} e {} " 
					,account.getAccount_id(),account.getAccount_branchid(),e);
	    	return AccountConstant.ACCOUNT_SEQ_FAILURE;

		}
		return seq;
	}

	/**
     * 更新转账金额
     * @param accountTo
     * @param money
     * @param tranLog
	 * @param toSeq 
	 * @param fromSeq 
	 * @param nowTime 
	 * @param nowDate 
	 * @param nowMillis 
	 * @param branchIdList 
	 * @return 
     */
    public Tranlog withdrawMoney(String accountTo, BigDecimal money, Tranlog tranLog, int fromSeq, Date nowDate, Time nowTime, long nowMillis) {
    	TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account to = transfer.readTransactionalAccount(accountTo);
    		//更新贷方余额和贷方科目余额
            Account at = new Account();
            at.setAccount_id(accountTo);
            at.setAccount_bale(money);
            transfer.transferTo(at);
            Sjno tf = new Sjno();
            tf.setSjno_id(to.getAccount_sjnoid());
            tf.setSjno_branchid(to.getAccount_branchid());
            tf.setSjno_bale(money);
            transfer.transferToSjno(tf);
    		Customer customerTo = new Customer();
       		customerTo.setCustomer_id(to.getAccount_custid());
       		customerTo.setCustomer_bale(money);
    		transfer.updateCustomerTo(customerTo);
    		Tranlist fromTranList = new Tranlist(nowDate,to.getAccount_branchid(), 
            		fromSeq, nowTime,to.getAccount_id(), money,
            		AccountConstant.TRANLIST_D, to.getAccount_id(),trancfgType,process_id,money);//写借方tranlist
            transfer.insertTranlist(fromTranList);
    		//插入tranlist记录
            Tranlist toTranList = new Tranlist(nowDate,to.getAccount_branchid(),
            		fromSeq,nowTime,to.getAccount_id(),money,
            		AccountConstant.TRANLIST_C,to.getAccount_id(),trancfgType,process_id,new BigDecimal(0));//写贷方tranlist
            transfer.insertTranlist(toTranList);
    		logger.info("DepositMoneyServiceImpl withdrawMoney insert to tranlist account {} success ", accountTo);
        	tranLog = new Tranlog(nowDate,to.getAccount_branchid(),fromSeq,trancfgType
        			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_SUCCESS,"",process_id);
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error("DepositMoneyServiceImpl withdrawMoney rollback accountTo {} e {}",accountTo,e);
	    	try {
				dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("DepositMoneyServiceImpl withdrawMoney rollback error accountTo {} e {}",accountTo,e);
			}
	    	return null;
		}
    	return tranLog;

	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public int getDatacfg_accnum() {
		return datacfg_accnum;
	}

	public void setDatacfg_accnum(int datacfg_accnum) {
		this.datacfg_accnum = datacfg_accnum;
	}

	public String getTrancfgType() {
		return trancfgType;
	}

	public void setTrancfgType(String trancfgType) {
		this.trancfgType = trancfgType;
	}

	public String getProcess_id() {
		return process_id;
	}

	public void setProcess_id(String process_id) {
		this.process_id = process_id;
	}

	public Trancfg getTf() {
		return tf;
	}

	public void setTf(Trancfg tf) {
		this.tf = tf;
	}
	
	
}
